/*
 * Copyright 2010 Michael Nitsche
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.mnit.basis.daten.struktur.schluessel;

import java.util.Iterator;

import de.mnit.basis.daten.struktur.S_LM_Basis;
import de.mnit.basis.daten.struktur.gruppe.Gruppe2;
import de.mnit.basis.daten.struktur.menge.Menge;
import de.mnit.basis.daten.struktur.menge.S_Menge;
import de.mnit.basis.daten.struktur.ordnen.Sortieren;
import de.mnit.basis.fehler.Fehler;
import de.mnit.basis.sys.Ausgabe;


/**
 * @author Michael Nitsche
 *
 * TODO Schutz hier benötigt? Nur wegen Schutz macht aber eine X-Klasse keinen Sinn
 */
public abstract class A_Schluessel_LM_Basis<TA,TB> implements S_Schluessel_LM<TA,TB> {

	protected S_LM_Basis<TB> objekte;
	private   S_Menge<TA> schluessel;
	protected boolean geschuetzt=false;


	public A_Schluessel_LM_Basis() {
		this.schluessel = Menge.neu();
	}


	public void sortieren(int... nach) {
		if(geschuetzt) throw Fehler.sicherheit.da_Geschuetzt();
		Sortieren.sortieren(this, nach);
	}

	public void umbenennen(TA schluessel_alt, TA schluessel_neu) {
		int pos=schluessel.gPosition(schluessel_alt);
		schluessel.setze(pos, schluessel_neu);
	}

	public boolean istLeer() {
		return schluessel.gLaenge()==0;
	}

	public int gLaenge() {
		return schluessel.gLaenge();
	}

	public int gBreite() {
		return 2;
	}

	public S_Menge<TA> gSchluessel() {
		return schluessel.kopie();
	}

	public Object gib(int spalte, int zeile) {
		Fehler.zahl.wenn_Ausserhalb(1, 2, spalte);
		if(spalte==1)
			return this.schluessel.gib(zeile);
		else
			return this.objekte.gib(zeile);
	}

	public TB gib(TA schluessel) {
		Integer pos=this.schluessel.gPosition(schluessel);
		if(pos==null)
			Fehler.liste.da_ElementFehlt(schluessel);
		return this.objekte.gib(pos);
	}

	public TB gib(TA schluessel, TB wennfehlt) {
		Integer pos=this.schluessel.gPosition(schluessel);
		return pos==null ? wennfehlt : this.objekte.gib(pos);
	}


	public Gruppe2<TA, TB> gib(int zeile) {
		return Gruppe2.neu(schluessel.gib(zeile), objekte.gib(zeile));
	}

	public Iterator<Gruppe2<TA, TB>> iterator() {
		return new Iterator<Gruppe2<TA,TB>>(){
			int naechster=1;
			public boolean hasNext() {
				return gLaenge()>=naechster;
			}
			public Gruppe2<TA, TB> next() {
				return Gruppe2.neu(schluessel.gib(naechster), objekte.gib(naechster++));
			}
			public void remove() {
				Fehler.sonstige.da_Untypisch();
			}
		};
	}

	public void plus(TA schluessel, TB objekt) {
		if(geschuetzt)
			throw Fehler.sicherheit.da_Geschuetzt();
		if(this.schluessel.gPosition(schluessel)!=null)
			Fehler.liste.da_ElementExistiert(schluessel);
		this.schluessel.plus(schluessel);
		this.objekte.plus(objekt);
	}

	public TB minus(TA schluessel) {
		if(geschuetzt)
			throw Fehler.sicherheit.da_Geschuetzt();
		Integer pos = this.schluessel.gPosition(schluessel);
		if(pos==null)
			Fehler.liste.da_ElementFehlt(schluessel);
		this.schluessel.minus(pos);
		return this.objekte.minus(pos);
	}

	public void ersetzen(TA schluessel, TB neu) {
		if(geschuetzt)
			throw Fehler.sicherheit.da_Geschuetzt();
		Integer pos = this.schluessel.gPosition(schluessel);
		if(pos==null)
			Fehler.liste.da_ElementFehlt(schluessel);
		this.objekte.setze(pos,neu);
	}

	public void setze(TA schluessel, TB objekt) {
		if(geschuetzt)
			throw Fehler.sicherheit.da_Geschuetzt();
		Integer pos = this.schluessel.gPosition(schluessel);
		if(pos==null)
			this.plus(schluessel, objekt);
		else
			this.ersetzen(schluessel,objekt);
	}

	public void vertauschen(int pos_a, int pos_b) {
		if(geschuetzt)
			throw Fehler.sicherheit.da_Geschuetzt();
		this.schluessel.vertauschen(pos_a, pos_b);
		this.objekte.vertauschen(pos_a, pos_b);
	}

	public void ausgeben() {
		Ausgabe.debugVersetzt(1, this);
	}

	public String zuTextDebug() {
		return this.toString();
	}

	public String toString() {
		StringBuilder sb=new StringBuilder();
		for(int z=1; z<=gLaenge(); z++)
			sb.append(this.gib(1, z)+","+this.gib(2, z)+"\n");
		return sb.toString();
	}

	public void schuetzen() {
		this.geschuetzt=true;
	}

	public boolean istGeschuetzt() {
		return this.geschuetzt;
	}

}
